package com.ygqh.baby.quartz;

import com.foxinmy.weixin4j.util.StringUtil;
import com.ygqh.baby.ao.YgQuartzJob;
import com.ygqh.baby.model.SchJob;
import com.ygqh.baby.po.ScheduleJob;
import com.ygqh.baby.quartz.listener.YgQuartzJobListener;
import com.ygqh.baby.service.ScheduleJobService;
import com.ygqh.baby.utils.DateConvertUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.matchers.KeyMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 任务调度公共类
 *
 * @author feizi
 * @remark
 * @time 2015-3-23下午3:04:12
 */
public class QuartzManager {

    private static Logger log = LoggerFactory.getLogger(QuartzManager.class);// 日志
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private ScheduleJobService scheduleJobService;
    private static SchedulerFactoryBean schedulerBean;
    private static ScheduleJobService schJobService;

    private static Boolean isInit = false;

    @Value("${project.type}")
    private String jobType;

    @PostConstruct
    public void init() {
        if (isInit) {
            return;
        }
        isInit = true;
        schedulerBean = schedulerFactoryBean;
        schJobService = scheduleJobService;
        List<ScheduleJob> jobs = schJobService.findJobs(SchJob.STATUS_RUNNING, null, null, true);
        if (CollectionUtils.isNotEmpty(jobs)) {
            jobs.parallelStream().forEach(job -> {
                if (CronExpression.isValidExpression(job.getCron()) && !job.isCornValid()) {
                    Date date = DateConvertUtils.addMinute(new Date(), 2);
                    String cron = DateConvertUtils.format(date, "ss mm HH dd MM ? yyyy");
                    job.setCron(cron);
                    job.setCronDate(date);
                }
                try {
                    addJob(job);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });

        }
    }

    public static CronTrigger createTriggerByJob(String jobName, String jobGroup, String cron) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(scheduleBuilder).build();
        }
        return trigger;

    }

    public static String getCronExpression(YgQuartzJob job, Date date) {
        String cron = null;

        // Date runDate = DateConvertUtils.addDay(new Date(), 15);
        // Date runDate = DateConvertUtils.addMinute(new Date(), 1);
        cron = DateConvertUtils.format(date, "ss mm HH dd MM ? yyyy");
        return cron;

    }

    /**
     * 添加任务
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public static void addJob(ScheduleJob scheduleJob) throws SchedulerException {
        if (scheduleJob == null || !SchJob.STATUS_RUNNING.equals(scheduleJob.getJobStatus())) {
            return;
        }
        Scheduler scheduler = schedulerBean.getScheduler();
        Boolean exitJob = exitJob(scheduler, scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger;
        if (exitJob) {

            // 按新的cronExpression表达式重新构建trigger
            trigger = createTriggerByJob(scheduleJob.getJobName(), scheduleJob.getJobGroup(), scheduleJob.getCron());
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(trigger.getKey(), trigger);
            return;
        }
        Class<? extends Job> jobClass = QuartzJobFactory.class;
        // if (scheduleJob.getJobName().startsWith(YgQuartzJob.Balance.name()))
        // {
        // jobClass = QuartzBalanceJob.class;
        // }

        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
        jobDetail.getJobDataMap().put("scheduleJob", scheduleJob);

        trigger = createTriggerByJob(scheduleJob.getJobName(), scheduleJob.getJobGroup(), scheduleJob.getCron());
        scheduler.scheduleJob(jobDetail, trigger);
        KeyMatcher<JobKey> matcher = KeyMatcher.keyEquals(jobDetail.getKey());
        scheduler.getListenerManager().addJobListener(new YgQuartzJobListener(), matcher);
        // 启动
        scheduler.start();
    }

    public static Boolean exitJob(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        JobDetail detail = scheduler.getJobDetail(jobKey);
        if (detail != null) {
            return true;
        }
        return false;

    }

    public static CronTrigger getTrigger(Scheduler scheduler, String jobName, String jobGroup) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        return trigger;

    }

    public static void updateScheduleJob(ScheduleJob scheduleJob) {
        if (scheduleJob != null && scheduleJob.getId() != null) {
            schJobService.update(scheduleJob);
        } else {
            schJobService.updateByJobKey(scheduleJob);
        }
    }

    /**
     * 更改任务 cron表达式
     *
     * @throws SchedulerException
     */
    public static void updateCron(Long jobId, String cron) throws SchedulerException {
        schJobService.updateCron(jobId, cron);
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return
     * @throws SchedulerException
     */
    public static List<ScheduleJob> getAllJob(String groupName, String jobName) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        GroupMatcher<JobKey> matcher;
        if (StringUtil.isNotBlank(groupName)) {
            matcher = GroupMatcher.groupEquals(groupName);
        } else {
            matcher = GroupMatcher.anyJobGroup();
        }
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);

        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
        for (JobKey jobKey : jobKeys) {
            if (StringUtil.isNotBlank(jobName) && !jobKey.getName().equals(jobName)) {
                continue;
            }
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                SchJob job = new SchJob();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                job.setDescription("触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCron(cronExpression);
                    job.setCronDate(DateConvertUtils.parse(cronExpression, DateConvertUtils.DATE_FORMAT_CRON));
                }
                jobList.add(job);
            }
            if (StringUtil.isNotBlank(jobName) && jobKey.getName().equals(jobName)) {
                break;
            }
        }
        return jobList;
    }

    public static List<ScheduleJob> getAllJob1() throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();

        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                ScheduleJob job = (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
                job.setDescription("触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCron(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public static List<SchJob> getRunningJob() throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<SchJob> jobList = new ArrayList<SchJob>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            SchJob job = new SchJob();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("触发器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCron(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }

    public static SchJob getJobByJobName(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return null;
        }
        List<? extends Trigger> list = scheduler.getTriggersOfJob(jobKey);

        SchJob job = new SchJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        if (CollectionUtils.isEmpty(list)) {
            return job;
        }

        String cronExpression = "";
        for (Trigger trigger : list) {
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                cronExpression = cronExpression + ";" + cronTrigger.getCronExpression();
            }
        }
        job.setCron(cronExpression);
        return job;
    }

    /**
     * 暂停一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public static void pauseJob(SchJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public static void resumeJob(SchJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除一个job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public static Boolean deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        return scheduler.deleteJob(jobKey);

    }

    /**
     * 立即执行job
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    public static void runAJobNow(SchJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新job时间表达式
     *
     * @param scheduleJob
     * @throws SchedulerException
     */
    /*
     * public static void updateJobCron(String jobName, String jobGroup, String
     * cron) throws SchedulerException { Scheduler scheduler =
     * schedulerBean.getScheduler(); TriggerKey triggerKey =
     * TriggerKey.triggerKey(jobName, jobName); CronTrigger trigger =
     * (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) {
     * trigger = createTriggerByJob(jobName, jobGroup, cron); }
     * CronScheduleBuilder scheduleBuilder =
     * CronScheduleBuilder.cronSchedule(cron); trigger =
     * trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(
     * scheduleBuilder).build(); scheduler.rescheduleJob(triggerKey, trigger); }
     */
    public static void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
        Scheduler scheduler = schedulerBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            addJob(scheduleJob);
        } else {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron());
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            scheduler.rescheduleJob(triggerKey, trigger);
        }

    }
}